<?php
// +----------------------------------------------------------------------
// | 授权系统 - 授权服务
// +----------------------------------------------------------------------
declare (strict_types=1);

namespace common\services\system;

use common\models\system\License;
use common\models\system\LicenseLog;
use Yii;

/**
 * 授权服务类
 * 提供授权生成、验证等核心功能
 */
class LicenseService
{
    /**
     * 生成授权码
     * @param int $customerId 客户ID
     * @param int $productId 产品ID
     * @param array $options 其他选项
     * @return string 授权码
     */
    public static function generateLicenseKey(int $customerId, int $productId, array $options = []): string
    {
        // 组合原始数据
        $data = [
            'customer' => $customerId,
            'product' => $productId,
            'timestamp' => time(),
            'random' => bin2hex(random_bytes(16))
        ];
        
        // 如果有过期时间，加入数据
        if (!empty($options['end_date'])) {
            $data['expire'] = strtotime($options['end_date']);
        }
        
        $json = json_encode($data);
        
        // 获取加密密钥
        $secretKey = Yii::$app->params['license_secret_key'] ?? 'default_secret_key_change_me';
        
        // AES加密
        $encrypted = EncryptService::aesEncrypt($json, $secretKey);
        
        // 计算校验码
        $checksum = substr(EncryptService::hash($encrypted), 0, 8);
        
        // 组合并格式化为授权码格式
        $rawKey = base64_encode($encrypted . $checksum);
        
        // 转换为授权码格式：XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
        return self::formatLicenseKey($rawKey);
    }
    
    /**
     * 格式化授权码
     * @param string $rawKey 原始密钥
     * @return string 格式化的授权码
     */
    private static function formatLicenseKey(string $rawKey): string
    {
        // 使用Base32编码（更易读，不包含容易混淆的字符）
        $base32 = self::base32Encode($rawKey);
        
        // 截取前25个字符并格式化
        $key = strtoupper(substr($base32, 0, 25));
        
        // 分割成5组，每组5个字符
        return substr($key, 0, 5) . '-' . 
               substr($key, 5, 5) . '-' . 
               substr($key, 10, 5) . '-' . 
               substr($key, 15, 5) . '-' . 
               substr($key, 20, 5);
    }
    
    /**
     * Base32编码
     */
    private static function base32Encode(string $data): string
    {
        $alphabet = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'; // 排除了容易混淆的字符
        $hash = hash('sha256', $data);
        $binary = '';
        
        for ($i = 0; $i < strlen($hash); $i += 2) {
            $binary .= str_pad(decbin(hexdec(substr($hash, $i, 2))), 8, '0', STR_PAD_LEFT);
        }
        
        $result = '';
        for ($i = 0; $i < strlen($binary); $i += 5) {
            $chunk = substr($binary, $i, 5);
            $result .= $alphabet[bindec(str_pad($chunk, 5, '0'))];
        }
        
        return $result;
    }
    
    /**
     * 创建授权
     * @param array $data 授权数据
     * @return License|null
     */
    public static function createLicense(array $data): ?License
    {
        $license = new License();
        
        // 生成授权码
        $license->license_key = self::generateLicenseKey(
            $data['customer_id'],
            $data['product_id'],
            ['end_date' => $data['end_date'] ?? null]
        );
        
        // 生成授权编号（可选）
        if (empty($data['license_no'])) {
            $license->license_no = 'LIC' . date('Ymd') . rand(1000, 9999);
        }
        
        // 填充其他数据
        $license->setAttributes($data);
        
        // 生成加密的授权数据（用于离线验证）
        $license->encrypted_data = self::generateEncryptedData($license);
        
        if ($license->save()) {
            return $license;
        }
        
        return null;
    }
    
    /**
     * 生成加密的授权数据
     * @param License $license 授权对象
     * @return string 加密数据
     */
    private static function generateEncryptedData(License $license): string
    {
        $data = [
            'license_key' => $license->license_key,
            'customer_id' => $license->customer_id,
            'product_id' => $license->product_id,
            'license_type' => $license->license_type,
            'license_level' => $license->license_level,
            'domain' => $license->domain,
            'ip_whitelist' => $license->ip_whitelist,
            'machine_code' => $license->machine_code,
            'features' => $license->features,
            'start_date' => $license->start_date,
            'end_date' => $license->end_date,
            'max_users' => $license->max_users,
            'max_sites' => $license->max_sites,
        ];
        
        $json = json_encode($data);
        $secretKey = Yii::$app->params['license_secret_key'] ?? 'default_secret_key_change_me';
        
        // AES加密
        $encrypted = EncryptService::aesEncrypt($json, $secretKey);
        
        // 添加签名
        $signature = EncryptService::hash($encrypted . $secretKey);
        
        return json_encode([
            'data' => $encrypted,
            'signature' => $signature,
            'version' => '1.0'
        ]);
    }
    
    /**
     * 验证授权
     * @param string $licenseKey 授权码
     * @param array $clientData 客户端数据
     * @return array 验证结果
     */
    public static function validateLicense(string $licenseKey, array $clientData = []): array
    {
        $startTime = microtime(true);
        
        // 查找授权
        $license = License::findOne(['license_key' => $licenseKey]);
        
        if (!$license) {
            self::logValidation($licenseKey, null, false, 'LICENSE_NOT_FOUND', '授权码不存在', $clientData);
            return [
                'success' => false,
                'error_code' => 'LICENSE_NOT_FOUND',
                'message' => '授权码不存在'
            ];
        }
        
        // 检查授权状态
        if ($license->status != License::STATUS_NORMAL) {
            self::logValidation($licenseKey, $license->id, false, 'LICENSE_DISABLED', '授权已被禁用', $clientData);
            return [
                'success' => false,
                'error_code' => 'LICENSE_DISABLED',
                'message' => '授权已被禁用'
            ];
        }
        
        // 检查是否过期
        if ($license->isExpired()) {
            // 更新状态
            $license->status = License::STATUS_EXPIRED;
            $license->save(false);
            
            self::logValidation($licenseKey, $license->id, false, 'LICENSE_EXPIRED', '授权已过期', $clientData);
            return [
                'success' => false,
                'error_code' => 'LICENSE_EXPIRED',
                'message' => '授权已过期'
            ];
        }
        
        // 检查域名
        if (!empty($clientData['domain'])) {
            if (!$license->isDomainAllowed($clientData['domain'])) {
                self::logValidation($licenseKey, $license->id, false, 'DOMAIN_NOT_ALLOWED', '域名不在授权范围内', $clientData, true);
                return [
                    'success' => false,
                    'error_code' => 'DOMAIN_NOT_ALLOWED',
                    'message' => '域名不在授权范围内'
                ];
            }
        }
        
        // 检查IP
        if (!empty($clientData['ip'])) {
            if (!$license->isIpAllowed($clientData['ip'])) {
                self::logValidation($licenseKey, $license->id, false, 'IP_NOT_ALLOWED', 'IP不在授权范围内', $clientData, true);
                return [
                    'success' => false,
                    'error_code' => 'IP_NOT_ALLOWED',
                    'message' => 'IP不在授权范围内'
                ];
            }
        }
        
        // 检查机器码
        if (!empty($license->machine_code) && !empty($clientData['machine_code'])) {
            if ($license->machine_code !== $clientData['machine_code']) {
                self::logValidation($licenseKey, $license->id, false, 'MACHINE_CODE_MISMATCH', '设备不匹配', $clientData, true);
                return [
                    'success' => false,
                    'error_code' => 'MACHINE_CODE_MISMATCH',
                    'message' => '设备不匹配'
                ];
            }
        }
        
        // 更新验证信息
        $license->updateValidateInfo(
            $clientData['ip'] ?? '',
            $clientData['domain'] ?? ''
        );
        
        $validateTime = intval((microtime(true) - $startTime) * 1000);
        
        // 记录验证日志
        self::logValidation($licenseKey, $license->id, true, 'SUCCESS', '验证成功', $clientData, false, $validateTime);
        
        return [
            'success' => true,
            'message' => '验证成功',
            'data' => [
                'license_type' => $license->license_type,
                'license_level' => $license->license_level,
                'end_date' => $license->end_date,
                'remaining_days' => $license->getRemainingDays(),
                'features' => $license->getFeatureList(),
                'max_users' => $license->max_users,
                'max_sites' => $license->max_sites,
            ]
        ];
    }
    
    /**
     * 记录验证日志
     */
    private static function logValidation(
        string $licenseKey,
        ?int $licenseId,
        bool $success,
        string $errorCode,
        string $errorMsg,
        array $clientData,
        bool $isAbnormal = false,
        int $validateTime = 0
    ): void {
        $log = new LicenseLog();
        $log->license_id = $licenseId;
        $log->license_key = $licenseKey;
        $log->validate_type = $clientData['validate_type'] ?? 'online';
        $log->validate_result = $success ? 1 : 0;
        $log->error_code = $errorCode;
        $log->error_msg = $errorMsg;
        $log->request_domain = $clientData['domain'] ?? null;
        $log->request_ip = $clientData['ip'] ?? null;
        $log->request_machine_code = $clientData['machine_code'] ?? null;
        $log->user_agent = $clientData['user_agent'] ?? null;
        $log->sdk_version = $clientData['sdk_version'] ?? null;
        $log->product_version = $clientData['product_version'] ?? null;
        $log->validate_time = $validateTime;
        $log->is_abnormal = $isAbnormal ? 1 : 0;
        $log->request_data = json_encode($clientData);
        $log->save();
    }
    
    /**
     * 续期授权
     * @param int $licenseId 授权ID
     * @param int $months 续期月数
     * @return bool
     */
    public static function renewLicense(int $licenseId, int $months): bool
    {
        $license = License::findOne($licenseId);
        if (!$license) {
            return false;
        }
        
        $currentEndDate = $license->end_date ? strtotime($license->end_date) : time();
        $newEndDate = date('Y-m-d H:i:s', strtotime("+{$months} months", $currentEndDate));
        
        $license->end_date = $newEndDate;
        $license->status = License::STATUS_NORMAL;
        
        return $license->save();
    }
}

